package com.qdlc.p2p.biz.web.interceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.util.TokenHelper;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Sets;
import com.opensymphony.xwork2.ActionInvocation;
import com.qdlc.p2p.biz.service.LogService;
import com.qdlc.p2p.biz.util.NotifyRequestUrl;
import com.qdlc.p2p.biz.util.UserLoginUtil;
import com.qdlc.p2p.common.constant.Constant;
import com.qdlc.p2p.common.constant.Global;
import com.qdlc.p2p.common.constant.LogType;
import com.qdlc.p2p.common.exception.BusinessException;
import com.qdlc.p2p.common.util.MD5Util;
import com.qdlc.p2p.common.util.StringUtil;
import com.qdlc.p2p.dal.dto.Log;
import com.qdlc.p2p.dal.dto.Operator;
import com.qdlc.p2p.dal.dto.User;

public class ActionInterceptor extends BaseInterceptor {

    private static final long serialVersionUID = 1L;

    private static final Logger log = Logger.getLogger(ActionInterceptor.class);

    @Resource
    private LogService logService;

    private static final String MSG = "msg";

    protected HttpServletRequest request;

    private static Set<String> greenUrl = Sets.newHashSet();

    static {
        greenUrl = NotifyRequestUrl.filtedNotifyUrl();
    }

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        Global.setIP(request);
        String actionName = invocation.getInvocationContext().getName();
        String className = invocation.getAction().getClass().getName();
        String url = request.getRequestURI();

        log.debug("++++ class:" + className + ",url:" + url);

        // 新浪回调 放行
        url = url.replaceAll(".action", "");
        if (url.contains("/tpp/tppSinaAction/") || greenUrl.contains(url)) {
            return invocation.invoke();
        }

        String newToken = "";
        try {
            checkUserLogin();
            // token校验及重新生成
            newToken = checkAndNewToken();
            if (StringUtil.isNotEmpty(newToken)) {
                return invocation.invoke();
            }
            return null;
        } catch (BusinessException e) {
            log.error(actionName, e);
            saveExceptionLog(className, actionName, e);
            if (e.getType() != BusinessException.TYPE_JSON) {
                message(e);
                return MSG; // 这里要分 前台 和 后台
            } else {
                Map<String, Object> data = new HashMap<String, Object>();
                data.put("result", false);
                data.put("msg", e.getMessage());
                data.put("url", e.getUrl());
                data.put("err_code", e.getErrorCode());
                data.put("token", newToken); // ajax请求出错，重新生成的token
                printJson(JSON.toJSONString(data));
                return null;
            }
        } catch (Exception e) {
            log.error(actionName, e);
            saveExceptionLog(className, actionName, e);
            message("抱歉,出现异常,请联系平台客服人员!", "");
            return MSG; // 这里要分 前台 和 后台
        }
    }

    public void message(BusinessException e) {
        String url = e.getUrl();
        String msg = e.getMessage();
        String buttonName = e.getButtonName();
        HttpServletRequest request = ServletActionContext.getRequest();
        String urlBack;
        if (e.getType() != 0 || StringUtil.isBlank(url)) {
            urlBack = "<a href='javascript:history.go(-1)'>返回</a>";
        } else {
            if (StringUtil.isNotBlank(buttonName)) {
                urlBack = "<a href='" + url + "'>" + buttonName + "</a>";
            } else {
                urlBack = "<a href='" + url + "'>返回</a>";
            }
        }
        request.setAttribute("backurl", urlBack);
        if (msg == null) {
            msg = "";
        }
        request.setAttribute("rsmsg", msg);
    }

    /**
     * 异常信息保存
     *
     * @param className  类命
     * @param methodName 方法名
     * @param exception  异常信息
     */
    protected void saveExceptionLog(String className, String methodName, Exception exception) {
        try {
            HttpServletRequest request = ServletActionContext.getRequest();
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            exception.printStackTrace(pw);

            Log log = new Log();
            log.setMethod(className + ":::" + methodName);
            log.setException(sw.toString());
            log.setParams(this.getAllParams(true));
            log.setRemoteAddr(Global.getIP());
            log.setRequestUri(request.getServletPath());
            log.setType(LogType.TYPE_EXCEPTION);
            if (request.getSession() != null) {
                Operator op = (Operator) request.getSession().getAttribute(Constant.SESSION_OPERATOR);
                if (op != null) {
                    log.setAddUser(op.getName());
                }
            }
            logService.addLog(log);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 提取参数
     *
     * @param safety 是否对密码过滤
     * @return 参数信息
     */
    protected String getAllParams(boolean safety) {
        HttpServletRequest request = ServletActionContext.getRequest();
        StringBuffer ps = new StringBuffer();
        Enumeration<?> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameter = (String) parameterNames.nextElement();
            String value = request.getParameter(parameter);
            if (StringUtil.isNotBlank(value)) {
                if (!safety || (safety && !parameter.contains("password") && !parameter.contains("pwd"))) { // 安全性
                    ps.append(parameter + "=" + value);
                    if (parameterNames.hasMoreElements()) {
                        ps.append("&");
                    }
                }
            }
        }
        return ps.toString();
    }

    private String checkAndNewToken() throws IOException {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpSession session = request.getSession();
        String url = request.getRequestURI();
        url = url.replaceAll(".action", "");
        String md5ReqUrl = MD5Util.MD5Encode(url, "utf-8");
        String newToken;
        // 参数中带有token则进行校验
        String reqToken = request.getParameter("_page_token");
        // 必须校验的URL（防止恶意篡改URL参数）
        List<String> urlList = new ArrayList<String>();
        urlList.add("/member/borrow/repay"); // 还款Action

        // 非必须校验的URL
        if (urlList.contains(url) || reqToken != null) {// 只有有token参数就进行校验
            // 检验时用 前一页面Url取token
            String refer = request.getHeader("referer");
            if (refer == null) refer = url;
            refer = StringUtil.isNotEmpty(reqToken) ? refer : url;

            refer = refer.replace("http://".concat(request.getHeader("host")), "");
            refer = refer.replace("https://".concat(request.getHeader("host")), "");
            refer = refer.replace(".action", "");
            refer = refer.replace(".html", "");
            refer = refer.replace(".htm", "");

            String md5Refer = MD5Util.MD5Encode(refer, "utf-8");
            // 前一页面的token
            String sesToken = (String) session.getAttribute("_page_token_" + md5Refer);

            // 用过后 重新生成新的token
            newToken = createToken(request, session, md5Refer);

            log.debug(refer + "  token = " + sesToken);

            if (StringUtils.isEmpty(reqToken)) {
                Map<String, Object> data = new HashMap<String, Object>();
                data.put("result", false);
                data.put("msg", "请勿恶意提交");
                data.put("token", newToken);
                printJson(JSON.toJSONString(data));
                return "";
            } else if (!reqToken.equals(sesToken)) {
                Map<String, Object> data = new HashMap<String, Object>();
                data.put("result", false);
                data.put("msg", "请勿重复提交");
                data.put("token", newToken);
                printJson(JSON.toJSONString(data));
                return "";
            }
        } else {
            // 本页面token 不存在 需重新生成
            String sesToken = (String) session.getAttribute("_page_token_" + md5ReqUrl);
            newToken = sesToken;
            if (StringUtil.isEmpty(sesToken)) {
                newToken = createToken(request, session, md5ReqUrl);
                log.debug(url + "  new_token = " + session.getAttribute("_page_token_" + md5ReqUrl));
            } else {
                request.setAttribute("_page_token", sesToken);
            }
        }
        return newToken;
    }

    private String createToken(HttpServletRequest request, HttpSession session, String md5Url) {
        session.removeAttribute("_page_token_" + md5Url);
        String token = md5Url + TokenHelper.generateGUID();
        session.setAttribute("_page_token_" + md5Url, token); // 校验时独立使用
        request.setAttribute("_page_token", token); // 页面通用 存放在request 如果放在session中 会被覆盖
        return token;
    }

    /**
     * 检测用户登录,如果登录失效,则跳转到登录页面
     */
    private void checkUserLogin() {
        HttpServletRequest request = ServletActionContext.getRequest();
        User user = UserLoginUtil.getLoginUser(request);
        if (user != null) {
            UserLoginUtil.heartbit(false);
        } else {
            HttpSession session = request.getSession();
            session.removeAttribute(Constant.SESSION_USER);
            session.removeAttribute("userCache");
            session.removeAttribute(Constant.SESSION_USER_IDENTIFY);
        }
    }

}